<?php

namespace App\Models\Service;

use Illuminate\Support\Carbon;

class VisionZenith
{
    const TOPIC_DOWN_IVS_TRIGGER = '/device/message/down/ivs_trigger';//发布触发识别
    const TOPIC_DOWN_GPIO_OUT = '/device/message/down/gpio_out';//发布IO输出事件
    const TOPIC_DOWN_SERIAL_DATA = '/device/message/down/serial_data';//发布串口数据转发
    const TOPIC_DOWN_SET_TIME = '/device/message/down/set_time';//发布设置系统时间
    const TOPIC_DOWN_SNAPSHOT = '/device/message/down/snapshot';//发布抓图事件
    const TOPIC_DOWN_WHITE_LIST_OPERATOR = '/device/message/down/white_list_operator';//发布白名单增删改查
    const TOPIC_DOWN_GET_DEVICE_TIMESTAMP = '/device/message/down/get_device_timestamp';//发布获取设备时间戳
    const TOPIC_DOWN_REBOOT_DEV = '/device/message/down/reboot_dev';//发布重启事件
    const TOPIC_DOWN_SET_CLOUD_CTRL = '/device/message/down/set_cloud_ctrl';//发布云台控制事件
    const TOPIC_DOWN_SET_IO_LOCK_STATUS = '/device/message/down/set_io_lock_status';//发布IO锁定事件
    const TOPIC_DOWN_GET_IO_LICK_STATUS = '/device/message/down/get_io_lock_status';//发布获取IO锁定状态
    const TOPIC_DOWN_GET_IO_STATUS = '/device/message/down/get_io_status';//发布获取IO状态

    const TOPIC_SUBSCRIBE_UP_GPIO_IN = '/device/message/up/gpio_in';//订阅IO输入事件
    const TOPIC_SUBSCRIBE_UP_SERIAL_DATA = '/device/message/up/serial_data';//订阅串口数据接收
    const TOPIC_SUBSCRIBE_UP_IVS_RESULT = '/device/message/up/ivs_result';//识别结果
    const TOPIC_SUBSCRIBE_UP_SNAPSHOT = '/device/message/up/snapshot';//抓图结果
    const TOPIC_SUBSCRIBE_UP_KEEP_ALIVE = '/device/message/up/keep_alive';//心跳
    const TOPIC_SUBSCRIBE_DOWN_IVS_TRIGGER_REPLY = '/device/message/down/ivs_trigger/reply';//发布触发识别回执
    const TOPIC_SUBSCRIBE_DOWN_GPIO_OUT_REPLY = '/device/message/down/gpio_out/reply';//IO输出事件回执
    const TOPIC_SUBSCRIBE_DOWN_SERIAL_DATA_REPLY = '/device/message/down/serial_data/reply';//发布串口数据转发回执
    const TOPIC_SUBSCRIBE_DOWN_SET_TIME_REPLY = '/device/message/down/set_time/reply';//发布设置系统时间回执
    const TOPIC_SUBSCRIBE_DOWN_SNAPSHOT_REPLY = '/device/message/down/snapshot/reply';//发布抓图事件回执
    const TOPIC_SUBSCRIBE_DOWN_WHITE_LIST_OPERATOR_REPLY = '/device/message/down/white_list_operator/reply';//发布白名单增删改查回执
    const TOPIC_SUBSCRIBE_DOWN_GET_DEVICE_TIMESTAMP_REPLY = '/device/message/down/get_device_timestamp/reply';//发布获取设备时间戳回执
    const TOPIC_SUBSCRIBE_DOWN_REBOOT_DEV_REPLY = '/device/message/down/reboot_dev/reply';//发布重启事件回执
    const TOPIC_SUBSCRIBE_DOWN_SET_CLOUD_CTRL_REPLY = '/device/message/down/set_cloud_ctrl/reply';//发布云台控制事件回执
    const TOPIC_SUBSCRIBE_DOWN_SET_IO_LOCK_STATUS_REPLY = '/device/message/down/set_io_lock_status/reply';//发布IO锁定事件回执
    const TOPIC_SUBSCRIBE_DOWN_GET_IO_LOCK_STATUS_REPLY = '/device/message/down/get_io_lock_status/reply';//发布获取IO锁定状态回执
    const TOPIC_SUBSCRIBE_DOWN_GET_IO_STATUS_REPLY = '/device/message/down/get_io_status/reply';//发布获取IO状态回执

    const PUBLISH_TOPICS = [
        self::TOPIC_DOWN_IVS_TRIGGER,
        self::TOPIC_DOWN_GPIO_OUT,
        self::TOPIC_DOWN_SERIAL_DATA,
        self::TOPIC_DOWN_SET_TIME,
        self::TOPIC_DOWN_SNAPSHOT,
        self::TOPIC_DOWN_WHITE_LIST_OPERATOR,
        self::TOPIC_DOWN_GET_DEVICE_TIMESTAMP,
        self::TOPIC_DOWN_REBOOT_DEV,
        self::TOPIC_DOWN_SET_CLOUD_CTRL,
        self::TOPIC_DOWN_SET_IO_LOCK_STATUS,
        self::TOPIC_DOWN_GET_IO_LICK_STATUS,
        self::TOPIC_DOWN_GET_IO_STATUS,
        self::TOPIC_SUBSCRIBE_UP_GPIO_IN,
        self::TOPIC_SUBSCRIBE_UP_SERIAL_DATA
    ];

    const SUBSCRIBE_TOPICS = [
        self::TOPIC_SUBSCRIBE_UP_IVS_RESULT,
        self::TOPIC_SUBSCRIBE_UP_SNAPSHOT,
        self::TOPIC_SUBSCRIBE_UP_KEEP_ALIVE,
        self::TOPIC_SUBSCRIBE_DOWN_IVS_TRIGGER_REPLY,
        self::TOPIC_SUBSCRIBE_DOWN_GPIO_OUT_REPLY,
        self::TOPIC_SUBSCRIBE_DOWN_SERIAL_DATA_REPLY,
        self::TOPIC_SUBSCRIBE_DOWN_SET_TIME_REPLY,
        self::TOPIC_SUBSCRIBE_DOWN_SNAPSHOT_REPLY,
        self::TOPIC_SUBSCRIBE_DOWN_WHITE_LIST_OPERATOR_REPLY,
        self::TOPIC_SUBSCRIBE_DOWN_GET_DEVICE_TIMESTAMP_REPLY,
        self::TOPIC_SUBSCRIBE_DOWN_REBOOT_DEV_REPLY,
        self::TOPIC_SUBSCRIBE_DOWN_SET_CLOUD_CTRL_REPLY,
        self::TOPIC_SUBSCRIBE_DOWN_SET_IO_LOCK_STATUS_REPLY,
        self::TOPIC_SUBSCRIBE_DOWN_GET_IO_LOCK_STATUS_REPLY,
        self::TOPIC_SUBSCRIBE_DOWN_GET_IO_STATUS_REPLY,
    ];

    public static function fmtIvsTriggerMsg(array $snPacket): array
    {
        $name = 'ivs_trigger';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {

            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            if ($sn = \Arr::get($rowSnDat, 'sn')) {
                $msgPacket[] = [
                    'id' => $msgID,
                    'sn' => $sn,
                    'name' => $name,
                    'version' => '1.0',
                    'timestamp' => time(),
                    'payload' => [
                        'type' => $name,
                        'body' => []
                    ]
                ];
            }
        }

        return $msgPacket;
    }

    public static function fmtGpioOutMsg(array $snPacket): array
    {
        $name = 'gpio_out';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {
            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            //0 开 , 1关
            $io = intval(\Arr::get($rowSnDat, 'io')) ? 1 : 0;
            $delay = intval(\Arr::get($rowSnDat, 'delay')) ?: 500;
            if ($sn = \Arr::get($rowSnDat, 'sn')) {
                $msgPacket[] = [
                    'id' => $msgID,
                    'sn' => $sn,
                    'name' => $name,
                    'version' => '1.0',
                    'timestamp' => time(),
                    'payload' => [
                        'type' => $name,
                        'body' => [
                            'delay' => $delay,
                            'io' => $io,//对应的IO 输出编号
                            'value' => 2
                        ]
                    ]
                ];
            }
        }
        return $msgPacket;
    }

    public static function fmtSerialDataMsg(array $snPacket): array
    {
        $name = 'serial_data';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {
            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            if ($sn = \Arr::get($rowSnDat, 'sn')) {

                $channelDat = \Arr::get($rowSnDat, 'channelDat') ?: [];
                $serialData = [];

                foreach ($channelDat as $rowChannelDat) {

                    $data = strval(\Arr::get($rowChannelDat, 'data'));
                    $base64Dat = base64_encode($data);
                    $dataLen = strlen($data);

                    $serialData[] = [
                        'serialChannel' => intval(\Arr::get($rowChannelDat, 'no')),
                        'data' => $base64Dat,
                        'dataLen' => $dataLen
                    ];
                }

                $msgPacket[] = [
                    'id' => $msgID,
                    'sn' => $sn,
                    'name' => $name,
                    'version' => '1.0',
                    'timestamp' => time(),
                    'payload' => [
                        'type' => $name,
                        'body' => [
                            'serialData' => $serialData
                        ]
                    ]
                ];
            }
        }

        return $msgPacket;
    }

    public static function fmtGetDeviceTimestampMsg(array $snPacket): array
    {
        $name = 'get_device_timestamp';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {
            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            if ($sn = \Arr::get($rowSnDat, 'sn')) {
                $msgPacket[] = [
                    'id' => $msgID,
                    'sn' => $sn,
                    'name' => $name,
                    'version' => '1.0',
                    'timestamp' => time(),
                    'payload' => [
                        'type' => $name,
                        'body' => []
                    ]
                ];
            }
        }

        return $msgPacket;
    }

    public static function fmtSetTimeMsg(array $snPacket): array
    {
        $name = 'set_time';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {
            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            if ($sn = \Arr::get($rowSnDat, 'sn')) {
                $now = now();
                $msgPacket[] = [
                    'id' => $msgID,
                    'sn' => $sn,
                    'name' => $name,
                    'version' => '1.0',
                    'timestamp' => time(),
                    'payload' => [
                        'type' => $name,
                        'body' => [
                            'year' => $now->year,
                            'month' => str_pad($now->month, 2, '0', STR_PAD_LEFT),
                            'day' => str_pad($now->day, 2, '0', STR_PAD_LEFT),
                            'hour' => str_pad($now->hour, 2, '0', STR_PAD_LEFT),
                            'min' => str_pad($now->minute, 2, '0', STR_PAD_LEFT),
                            'sec' => str_pad($now->second, 2, '0', STR_PAD_LEFT),
                        ]
                    ]
                ];
            }
        }

        return $msgPacket;
    }

    public static function fmtSnapshotMsg(array $snPacket): array
    {
        $name = 'snapshot';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {
            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            if ($sn = \Arr::get($rowSnDat, 'sn')) {
                $msgPacket[] = [
                    'id' => $msgID,
                    'sn' => $sn,
                    'name' => $name,
                    'version' => '1.0',
                    'timestamp' => time(),
                    'payload' => [
                        'type' => $name,
                        'body' => []
                    ]
                ];
            }
        }

        return $msgPacket;
    }

    public static function fmtRebootDevMsg(array $snPacket): array
    {
        $name = 'reboot_dev';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {
            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            if ($sn = \Arr::get($rowSnDat, 'sn')) {
                $msgPacket[] = [
                    'id' => $msgID,
                    'sn' => $sn,
                    'name' => $name,
                    'version' => '1.0',
                    'timestamp' => time(),
                    'payload' => [
                        'type' => $name,
                        'body' => []
                    ]
                ];
            }
        }

        return $msgPacket;
    }

    public static function fmtSelectWhiteListMsg(array $snPacket): array
    {
        $name = 'white_list_operator';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {
            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            if ($sn = \Arr::get($rowSnDat, 'sn')) {

                $carNos = \Arr::get($rowSnDat, 'carNos');
                $carNos = array_filter(array_unique(explode(',', $carNos)));

                foreach ($carNos as $carNo) {
                    $msgPacket[] = [
                        'id' => $msgID,
                        'sn' => $sn,
                        'name' => $name,
                        'version' => '1.0',
                        'timestamp' => time(),
                        'payload' => [
                            'type' => $name,
                            'body' => [
                                'operator_type' => 'select',
                                'plate' => $carNo,
                                'sub_type' => 'plate'
                            ]
                        ]
                    ];
                }
            }
        }

        return $msgPacket;
    }

    public static function fmtDelWhiteListMsg(array $snPacket): array
    {
        $name = 'white_list_operator';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {
            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            if ($sn = \Arr::get($rowSnDat, 'sn')) {

                $carNos = \Arr::get($rowSnDat, 'carNos');
                $carNos = array_filter(array_unique(explode(',', $carNos)));

                if (!count($carNos)) {
                    $carNos = '';
                }

                $msgPacket[] = [
                    'id' => $msgID,
                    'sn' => $sn,
                    'name' => $name,
                    'version' => '1.0',
                    'timestamp' => time(),
                    'payload' => [
                        'type' => $name,
                        'body' => [
                            'operator_type' => 'delete',
                            'plate' => $carNos
                        ]
                    ]
                ];
            }
        }

        return $msgPacket;
    }

    public static function fmtStoreWhiteListMsg(array $snPacket): array
    {
        $name = 'white_list_operator';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {
            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            if ($sn = \Arr::get($rowSnDat, 'sn')) {

                $carNos = \Arr::get($rowSnDat, 'carNos');
                $carNos = array_filter(array_unique(explode(',', $carNos)));

                $createdAt = strval(\Arr::get($rowSnDat, 'created_at'));
                $enabledAt = strval(\Arr::get($rowSnDat, 'enabled_at'));
                $overdueAt = strval(\Arr::get($rowSnDat, 'overdue_at'));
                $segTime = strval(\Arr::get($rowSnDat, 'seg_time'));
                $enable = intval(\Arr::get($rowSnDat, 'enable')) ? 1 : 0;
                $timeSegEnable = intval(\Arr::get($rowSnDat, 'time_seg_enable')) ? 1 : 0;
                $needAlarm = intval(\Arr::get($rowSnDat, 'need_alarm')) ? 1 : 0;
                $vehicleCode = strval(\Arr::get($rowSnDat, 'code'));
                $vehicleComment = strval(\Arr::get($rowSnDat, 'comment'));
                $customerID = strval(\Arr::get($rowSnDat, 'id'));

                foreach ($carNos as $carNo) {

                    $msgPacket[] = [
                        'id' => $msgID,
                        'sn' => $sn,
                        'name' => $name,
                        'version' => '1.0',
                        'timestamp' => time(),
                        'payload' => [
                            'type' => $name,
                            'body' => [
                                'operator_type' => 'update_or_add',
                                'dldb_rec' => [
                                    'create_time' => $createdAt,
                                    'enable_time' => $enabledAt,
                                    'overdue_time' => $overdueAt,
                                    'enable' => $enable,
                                    'plate' => $carNo,
                                    'time_seg_enable' => $timeSegEnable,
                                    'seg_time' => $segTime,
                                    'need_alarm' => $needAlarm,
                                    'vehicle_code' => $vehicleCode,
                                    'vehicle_comment' => $vehicleComment,
                                    'customer_id' => $customerID
                                ]
                            ]
                        ]
                    ];
                }
            }
        }

        return $msgPacket;
    }

    public static function fmtSetCloudCtrlMsg(array $snPacket): array
    {
        $name = 'set_cloud_ctrl';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {
            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            if ($sn = \Arr::get($rowSnDat, 'sn')) {

                $type = intval(\Arr::get($rowSnDat, 'type'));
                $value = intval(\Arr::get($rowSnDat, 'value'));

                $msgPacket[] = [
                    'id' => $msgID,
                    'sn' => $sn,
                    'name' => $name,
                    'version' => '1.0',
                    'timestamp' => time(),
                    'payload' => [
                        'type' => $name,
                        'body' => [
                            'type' => $type,
                            'value' => $value
                        ]
                    ]
                ];
            }
        }

        return $msgPacket;
    }

    public static function fmtSetIOLockStatusMsg(array $snPacket): array
    {
        $name = 'set_io_lock_status';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {
            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            if ($sn = \Arr::get($rowSnDat, 'sn')) {

                $ioout = intval(\Arr::get($rowSnDat, 'ioout'));
                $status = intval(\Arr::get($rowSnDat, 'status'));

                $msgPacket[] = [
                    'id' => $msgID,
                    'sn' => $sn,
                    'name' => $name,
                    'version' => '1.0',
                    'timestamp' => time(),
                    'payload' => [
                        'type' => $name,
                        'body' => [
                            'ioout' => $ioout,
                            'status' => $status
                        ]
                    ]
                ];
            }
        }

        return $msgPacket;
    }

    public static function fmtGetIOLockStatusMsg(array $snPacket): array
    {
        $name = 'get_io_lock_status';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {
            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            if ($sn = \Arr::get($rowSnDat, 'sn')) {
                $msgPacket[] = [
                    'id' => $msgID,
                    'sn' => $sn,
                    'name' => $name,
                    'version' => '1.0',
                    'timestamp' => time(),
                    'payload' => [
                        'type' => $name,
                        'body' => []
                    ]
                ];
            }
        }

        return $msgPacket;
    }

    public static function fmtGetIOStatusMsg(array $snPacket): array
    {
        $name = 'get_io_status';
        $msgPacket = [];

        foreach ($snPacket as $rowSnDat) {
            $msgID = \Arr::get($rowSnDat, 'msg_id') ?: self::getMsgID();
            if ($sn = \Arr::get($rowSnDat, 'sn')) {

                $type = intval(\Arr::get($rowSnDat, 'type'));
                $gpio = intval(\Arr::get($rowSnDat, 'gpio'));

                $msgPacket[] = [
                    'id' => $msgID,
                    'sn' => $sn,
                    'name' => $name,
                    'version' => '1.0',
                    'timestamp' => time(),
                    'payload' => [
                        'type' => $name,
                        'body' => [
                            'type' => $type,
                            'gpio' => $gpio
                        ]
                    ]
                ];
            }
        }

        return $msgPacket;
    }

    static public function getMsgID(): string
    {
        return \Str::random();
    }

    //同步时间
    public static function getSyncTimeByteData(Carbon $datetime = null): string
    {
        $DA = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $VR = strtoupper(dechex(100));
        $PN = strtoupper(dechex(65535));
        $CMD = strtoupper(str_pad(dechex(5), 2, 0, STR_PAD_LEFT));
        $DL = strtoupper(str_pad(dechex(8), 2, 0, STR_PAD_LEFT));
        $datetime = $datetime ?: now();
        $Y = strtoupper(str_pad(bin2hex(pack('v*',$datetime->year)), 4, 0, STR_PAD_LEFT));
        $M = strtoupper(str_pad(dechex($datetime->month), 2, 0, STR_PAD_LEFT));
        $D = strtoupper(str_pad(dechex($datetime->day), 2, 0, STR_PAD_LEFT));
        $W = strtoupper(dechex($datetime->dayOfWeek));
        if ($W >= 7) {
            $W = 1;
        } else {
            $W += 1;
        }
        $W = strtoupper(str_pad(dechex($W), 2, 0, STR_PAD_LEFT));
        $H = strtoupper(str_pad(dechex($datetime->hour), 2, 0, STR_PAD_LEFT));
        $N = strtoupper(str_pad(dechex($datetime->minute), 2, 0, STR_PAD_LEFT));
        $S = strtoupper(str_pad(dechex($datetime->second), 2, 0, STR_PAD_LEFT));
        $CRC = Crc16::calculate($DA . $VR . $PN . $CMD . $DL . $Y . $M . $D . $W . $H . $N . $S);
        return hex2bin($DA . $VR . $PN . $CMD . $DL . $Y . $M . $D . $W . $H . $N . $S . $CRC);
    }

    //废案
    public static function getRemoteCtrlByteData(int $act = 0, int $delayTime = 5)
    {
        if (!in_array($act, [0, 1, 2], true)) {
            $act = 254;
        }

        $delayTime = abs($delayTime);
        $delayTime = min(254, $delayTime);

        $DA = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $VR = strtoupper(dechex(100));
        $PN = strtoupper(dechex(65535));
        $CMD = strtoupper(str_pad(dechex(15), 2, 0, STR_PAD_LEFT));
        $DL = strtoupper(str_pad(dechex(6), 2, 0, STR_PAD_LEFT));
        $CH = strtoupper(str_pad(dechex($act), 2, 0, STR_PAD_LEFT));
        $OF = strtoupper(str_pad(dechex(1), 2, 0, STR_PAD_LEFT));
        $RESERVED = strtoupper(str_pad(dechex(0), 6, 0, STR_PAD_LEFT));
        $OT = strtoupper(str_pad(dechex($delayTime), 2, 0, STR_PAD_LEFT));
        $CRC = Crc16::calculate($DA . $VR . $PN . $CMD . $DL . $CH . $OF . $RESERVED . $OT);
        return hex2bin($DA . $VR . $PN . $CMD . $DL . $CH . $OF . $RESERVED . $OT . $CRC);
    }

    //屏幕亮度
    public static function getScreenLightByteData(int $percent)
    {
        $percent = max(min($percent, 100), 10);
        $DA = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $VR = strtoupper(dechex(100));
        $PN = strtoupper(dechex(65535));
        $CMD = strtoupper(str_pad(dechex(12), 2, 0, STR_PAD_LEFT));
        $DL = strtoupper(str_pad(dechex(1), 2, 0, STR_PAD_LEFT));
        $LIGHT = strtoupper(str_pad(dechex($percent), 2, 0, STR_PAD_LEFT));
        $CRC = Crc16::calculate($DA . $VR . $PN . $CMD . $DL . $LIGHT);
        return hex2bin($DA . $VR . $PN . $CMD . $DL . $LIGHT . $CRC);
    }

    //播报音量
    public static function getAdjustVolumeByteData(int $percent)
    {
        $percent = max(min($percent, 100), 10);
        $DA = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $VR = strtoupper(dechex(100));
        $PN = strtoupper(dechex(65535));
        $DL = strtoupper(str_pad(dechex(1), 2, 0, STR_PAD_LEFT));
        $CMD = strtoupper(str_pad(dechex(13), 2, 0, STR_PAD_LEFT));
        $VOL = strtoupper(str_pad(dechex($percent), 2, 0, STR_PAD_LEFT));
        $CRC = Crc16::calculate($DA . $VR . $PN . $CMD . $DL . $VOL);
        return hex2bin($DA . $VR . $PN . $CMD . $DL . $VOL . $CRC);
    }

    //初始化
    public static function getInitializationByteData()
    {
        $DA = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $VR = strtoupper(dechex(100));
        $PN = strtoupper(dechex(65535));
        $DL = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $CMD = '1E';
        $CRC = Crc16::calculate($DA . $VR . $PN . $CMD . $DL);
        return hex2bin($DA . $VR . $PN . $CMD . $DL . $CRC);
    }

    //脱机临时车模式
    public static function getOfflineTempVehicleScopeByteData(int $mode)
    {
        if (!in_array($mode, [0, 1, 2, 3], true)) {
            $mode = 1;
        }

        $DA = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $VR = strtoupper(dechex(100));
        $PN = strtoupper(dechex(65535));
        $CMD = 'D3';
        $TCA = strtoupper(str_pad(dechex($mode), 2, 0, STR_PAD_LEFT));
        $DL = strtoupper(str_pad(dechex(1), 2, 0, STR_PAD_LEFT));
        $CRC = Crc16::calculate($DA . $VR . $PN . $CMD . $DL . $TCA);
        return hex2bin($DA . $VR . $PN . $CMD . $DL . $TCA . $CRC);
    }

    //语音播报
    public static function getPlayAudioByteData(int $mode, string $text)
    {
        $DA = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $VR = strtoupper(dechex(100));
        $PN = strtoupper(dechex(65535));
        $CMD = '30';
        if (!in_array($mode, [0, 1, 2], true)) {
            $mode = 1;
        }
        $OPT = strtoupper(str_pad(dechex($mode), 2, 0, STR_PAD_LEFT));
        $TEXT = self::string2AsciiHexStr($text);
        $textLen = ceil(strlen($TEXT) / 2);
        $DL = strtoupper(str_pad(dechex($textLen + 1), 2, 0, STR_PAD_LEFT));
        $CRC = Crc16::calculate($DA . $VR . $PN . $CMD . $DL . $OPT . $TEXT);
        return hex2bin($DA . $VR . $PN . $CMD . $DL . $OPT . $TEXT . $CRC);
    }

    public static function getStopAudioByteData()
    {
        $DA = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $VR = strtoupper(dechex(100));
        $PN = strtoupper(dechex(65535));
        $CMD = '31';
        $DL = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $CRC = Crc16::calculate($DA . $VR . $PN . $CMD . $DL);
        return hex2bin($DA . $VR . $PN . $CMD . $DL . $CRC);
    }

    public static function getDownloadTmpTxtByteData(string $text, int $TWID = 0, int $ETM = 0, int $ETS = 1, int $GST = 0, int $DT = 5, int $FINDEX = 3, int $DRS = 0)
    {
        $DA = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $VR = strtoupper(dechex(100));
        $PN = strtoupper(dechex(65535));
        $CMD = '62';
        $TEXT = self::string2AsciiHexStr($text);
        $TWID = strtoupper(str_pad(dechex($TWID), 2, 0, STR_PAD_LEFT));
        $ETM = strtoupper(str_pad(dechex($ETM), 2, 0, STR_PAD_LEFT));
        $ETS = strtoupper(str_pad(dechex($ETS), 2, 0, STR_PAD_LEFT));
        $GST = strtoupper(str_pad(dechex($GST), 2, 0, STR_PAD_LEFT));
        $DT = strtoupper(str_pad(dechex($DT), 2, 0, STR_PAD_LEFT));//
        $FINDEX = strtoupper(str_pad(dechex($FINDEX), 2, 0, STR_PAD_LEFT));
        $DRS = strtoupper(str_pad(dechex($DRS), 2, 0, STR_PAD_LEFT));
        $RA1 = strtoupper(str_pad(dechex(1), 4, 0, STR_PAD_LEFT));//
        $TC = strtoupper(str_pad(dechex(255), 2, 0, STR_PAD_LEFT)) . strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT)) . strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT)) . strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $RA2 = strtoupper(str_pad(dechex(0), 8, 0, STR_PAD_LEFT));
        $R1 = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $textLen = ceil(strlen($TEXT) / 2);
        $DL = strtoupper(str_pad(dechex($textLen + 19), 2, 0, STR_PAD_LEFT));
        $TL = strtoupper(str_pad(dechex($textLen), 2, 0, STR_PAD_LEFT));
        $CRC = Crc16::calculate($DA . $VR . $PN . $CMD . $DL . $TWID . $ETM . $ETS . $GST . $DT . $RA1 . $FINDEX . $DRS . $TC . $RA2 . $TL . $R1 . $TEXT);
        return hex2bin($DA . $VR . $PN . $CMD . $DL . $TWID . $ETM . $ETS . $GST . $DT . $RA1 . $FINDEX . $DRS . $TC . $RA2 . $TL . $R1 . $TEXT . $CRC);
    }

    //纯下载不显示
    public static function getDownloadAdvertisementTxtByteData(string $text, int $TWID = 0, int $ETM = 21, int $ETS = 1, int $DT = 5, int $FINDEX = 3)
    {
        $DA = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $VR = strtoupper(dechex(100));
        $PN = strtoupper(dechex(65535));
        $CMD = '67';
        $TEXT = self::string2AsciiHexStr($text);
        $TWID = strtoupper(str_pad(dechex($TWID), 2, 0, STR_PAD_LEFT));
        $R1 = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $R2 = '0C';
        $ETM = strtoupper(str_pad(dechex($ETM), 2, 0, STR_PAD_LEFT));
        $ETS = strtoupper(str_pad(dechex($ETS), 2, 0, STR_PAD_LEFT));
        $R3 = '00';
        $DT = strtoupper(str_pad(dechex($DT), 2, 0, STR_PAD_LEFT));//
        $FINDEX = strtoupper(str_pad(dechex($FINDEX), 2, 0, STR_PAD_LEFT));
        $RA1 = '1501';//
        $TC = strtoupper(str_pad(dechex(255), 2, 0, STR_PAD_LEFT)) . strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT)) . strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT)) . strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $RA2 = strtoupper(str_pad(dechex(0), 8, 0, STR_PAD_LEFT));
        $textLen = ceil(strlen($TEXT) / 2);
        $DL = strtoupper(str_pad(dechex($textLen + 20), 2, 0, STR_PAD_LEFT));
        $TL = strtoupper(str_pad(dechex($textLen), 2, 0, STR_PAD_LEFT));
        $CRC = Crc16::calculate($DA . $VR . $PN . $CMD . $DL . $TWID . $R1 . $R2 . $ETM . $ETS . $R3 . $DT . $RA1 . $FINDEX . $TC . $RA2 . $TL . $R3 . $TEXT);
        return hex2bin($DA . $VR . $PN . $CMD . $DL . $TWID . $R1 . $R2 . $ETM . $ETS . $R3 . $DT . $RA1 . $FINDEX . $TC . $RA2 . $TL . $R3 . $TEXT . $CRC);
    }

    public static function getDisplayAdvertisementTxtByteData(int $TWID = 0, int $FID = 0)
    {
        $DA = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $VR = strtoupper(dechex(100));
        $PN = strtoupper(dechex(65535));
        $CMD = '68';
        $TWID = strtoupper(str_pad(dechex($TWID), 2, 0, STR_PAD_LEFT));
        $FID = strtoupper(str_pad(dechex($FID), 2, 0, STR_PAD_LEFT));
        $DL = strtoupper(str_pad(dechex(2), 2, 0, STR_PAD_LEFT));
        $CRC = Crc16::calculate($DA . $VR . $PN . $CMD . $DL . $TWID . $FID);
        return hex2bin($DA . $VR . $PN . $CMD . $DL . $TWID . $FID . $CRC);
    }

    /**
     * @param array $txtPacket 文字包 最大key值为3 , key为行号,value为显示内容
     * @param int $mode
     * @param int $GST
     *
     */
    public static function getDisplayWithVoiceByteData(array $txtPacket, string $VOICE_TXT = null, int $mode = 0, int $GST = 0, int $DM = 0, int $DT = 5)
    {
        $DA = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $VR = strtoupper(dechex(100));
        $PN = strtoupper(dechex(65535));
        $CMD = '6F';
        $TEXT_CONTEXT_NUMBER = count($txtPacket);
        $TEXT_CONTEXT = '';
        $RAW_VOICE_TXT = '';
        $SF = strtoupper(str_pad(dechex($mode), 2, 0, STR_PAD_LEFT));
        $GST = strtoupper(str_pad(dechex($GST), 2, 0, STR_PAD_LEFT));
        foreach ($txtPacket as $lineNo => $lineTxt) {
            $TEXT_CONTEXT .= strtoupper(str_pad(dechex($lineNo), 2, 0, STR_PAD_LEFT));
            $TEXT_CONTEXT .= strtoupper(str_pad(dechex($DM), 2, 0, STR_PAD_LEFT));//显示模式
            $TEXT_CONTEXT .= strtoupper(str_pad(dechex(1), 2, 0, STR_PAD_LEFT));//显示速度
            $TEXT_CONTEXT .= strtoupper(str_pad(dechex($DT), 2, 0, STR_PAD_LEFT));//停留时间
            $TEXT_CONTEXT .= strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));//显示次数
            $TEXT_CONTEXT .= strtoupper(str_pad(dechex(3), 2, 0, STR_PAD_LEFT));//字体
            $TEXT_CONTEXT .= strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));//保留
            $TEXT_CONTEXT .= strtoupper(str_pad(dechex(255), 2, 0, STR_PAD_LEFT)) . strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT)) . strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT)) . strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
            $asciiTxt = self::string2AsciiHexStr($lineTxt);
            $TEXT_CONTEXT .= strtoupper(str_pad(dechex(ceil(strlen($asciiTxt) / 2)), 2, 0, STR_PAD_LEFT));
            $TEXT_CONTEXT .= $asciiTxt;
            if ($TEXT_CONTEXT_NUMBER === $lineNo + 1) {
                $TEXT_CONTEXT .= '00';
            } else {
                $TEXT_CONTEXT .= '0D';
            }
            $RAW_VOICE_TXT .= $asciiTxt;
        }
        $VF = '0A';
        $VOICE_TXT = $VOICE_TXT ? self::string2AsciiHexStr($VOICE_TXT) : $RAW_VOICE_TXT;
        $VTL = strtoupper(str_pad(dechex(ceil(strlen($VOICE_TXT) / 2)), 2, 0, STR_PAD_LEFT));
        $TEXT_CONTEXT_NUMBER = strtoupper(str_pad(dechex($TEXT_CONTEXT_NUMBER), 2, 0, STR_PAD_LEFT));
        $DL = strtoupper(str_pad(dechex(ceil(strlen($TEXT_CONTEXT) / 2) + 14), 2, 0, STR_PAD_LEFT));
        $VOICE_TXT .= '00';
        $CRC = Crc16::calculate($DA . $VR . $PN . $CMD . $DL . $SF . $GST . $TEXT_CONTEXT_NUMBER . $TEXT_CONTEXT . $VF . $VTL . $VOICE_TXT);
        return hex2bin($DA . $VR . $PN . $CMD . $DL . $SF . $GST . $TEXT_CONTEXT_NUMBER . $TEXT_CONTEXT . $VF . $VTL . $VOICE_TXT . $CRC);
    }

    public static function getRemainingViewByteData(int $NUM, int $mode = 1, int $ST = 0)
    {
        $DA = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $VR = strtoupper(dechex(100));
        $PN = strtoupper(dechex(65535));
        $CMD = 'E3';
        $EM = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $ETM = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $SF = strtoupper(str_pad(dechex($mode), 2, 0, STR_PAD_LEFT));
        $ST = strtoupper(str_pad(dechex($ST), 2, 0, STR_PAD_LEFT));
        $NI = strtoupper(str_pad(dechex(0), 2, 0, STR_PAD_LEFT));
        $NUM = strtoupper(str_pad((bin2hex(pack('V*', $NUM))), 8, 0, STR_PAD_LEFT));
        $DL = strtoupper(str_pad(dechex(9), 2, 0, STR_PAD_LEFT));//写死为9
        $CRC = Crc16::calculate($DA . $VR . $PN . $CMD . $DL . $SF . $EM . $ETM . $ST . $NI . $NUM);
        return hex2bin($DA . $VR . $PN . $CMD . $DL . $SF . $EM . $ETM . $ST . $NI . $NUM . $CRC);
    }

    private static function string2AsciiHexStr($rawString): string
    {
        $rawString = mb_convert_encoding($rawString, 'gb2312');
        $asciiStr = '';
        for ($i = 0; $i < strlen($rawString); $i++) {
            $char = substr($rawString, $i, 1);
            $asciiChar = ord($char);
            $asciiStr .= strtoupper(str_pad(dechex($asciiChar), 2, 0, STR_PAD_LEFT));
        }
        return $asciiStr;
    }
}
